package com.numbersix.ant.locetask;

import java.io.File;
import java.io.FileWriter;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

/**
 * 
 */
public class LOCECounter extends Task {
	
	private List filesetList = new ArrayList(5);
	private Boolean verbose = Boolean.TRUE;
	private String reportLocation;
	
	public LOCECounter() {
		super();
	}
	
	public void execute() {
		log("Beginning lines of code equivilant count:");
		
		long javaCount = 0;
		long xmlCount = 0;
		long dtdCount = 0;
		long propertiesCount = 0;
		
		Map holderMap = new HashMap(10);
		LOCEHolder fileSetHolder;
		String fileSetDescription;
		FileSet fileSet = null;		
		Iterator i = filesetList.iterator();
		while (i.hasNext()) {		
			fileSet = (FileSet)i.next();
			fileSetDescription =  fileSet.getDescription();
			fileSetHolder = new LOCEHolder(fileSetDescription);
			JavaLOCECounter javaCounter = new JavaLOCECounter();
			XMLLOCECounter xmlCounter = new XMLLOCECounter();
			DTDLOCECounter dtdCounter = new DTDLOCECounter();
			PropertiesLOCECounter propertiesCounter = new PropertiesLOCECounter();
			fileSetHolder.addCounter(javaCounter);
			fileSetHolder.addCounter(xmlCounter);
			fileSetHolder.addCounter(dtdCounter);
			fileSetHolder.addCounter(propertiesCounter);
			holderMap.put(fileSetDescription, fileSetHolder);
			
			log("Counting " + fileSet.getDescription() + " files...");
			DirectoryScanner ds = fileSet.getDirectoryScanner(getProject());
			File fromDir = ds.getBasedir();
			String[] files = ds.getIncludedFiles();
			if (files != null) {
				String fileName = null;
				log("Processing " + files.length + " files");
				for (int j = 0; j < files.length; j++) {
					try {
						fileName = files[j];
						if ((fileName.endsWith(".java"))
						|| (fileName.endsWith(".jpf"))
						|| (fileName.endsWith("java.vm"))) {
							javaCounter.processFile(new File(fromDir, fileName));							
							
						} else if ((fileName.endsWith(".xml")) 
						|| (fileName.endsWith(".xmi"))
						|| (fileName.endsWith(".xsd"))
						|| (fileName.endsWith(".xml.vm"))
						|| (fileName.endsWith(".jspf"))
						|| (fileName.endsWith(".jsp"))) {							
							xmlCounter.processFile(new File(fromDir, fileName));							
							
						} else if (fileName.endsWith(".dtd")) {							
							dtdCounter.processFile(new File(fromDir, fileName));							
							
						} else if (fileName.endsWith(".properties")) {							
							propertiesCounter.processFile(new File(fromDir, fileName));							
								
						}  else {
							log("No valid parser found for file: " + fileName);
							
						}
						
					} catch (Exception e) {
						e.printStackTrace();	
					}
				}
			}
			
			//add up ongoing overall totals:
			javaCount += javaCounter.getSetCount();
			xmlCount += xmlCounter.getSetCount();
			dtdCount += dtdCounter.getSetCount();
			propertiesCount += propertiesCounter.getSetCount();
		}
		
		if (getReportLocation() != null) {			
			File report = new File(getReportLocation() + "/loce-report.html");
			log("Generating report to file: " + report.getAbsolutePath());
			
			//TODO: externalize to velocity:
			try {
				Properties props = new Properties();
			    String className = ClasspathResourceLoader.class.getName();
			    props.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER
			    		          + ".class", className);
			    props.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath");			    
				VelocityEngine ve = new VelocityEngine();
				ve.init(props);
				Template template = ve.getTemplate("loce-report.html.vm");
				FileWriter fw = new FileWriter(report);
				
				VelocityContext vc = new VelocityContext();
				vc.put("loceHolderList", holderMap.values());
				vc.put("completionTime", new Date().toString());
				vc.put("overallJavaCount", new Long(javaCount));
				vc.put("overallXmlCount", new Long(xmlCount));
				vc.put("overallDtdCount", new Long(dtdCount));
				vc.put("overallPropertiesCount", new Long(propertiesCount));
				long overallCount = javaCount + xmlCount + dtdCount + propertiesCount;
				vc.put("overallCount", new Long(overallCount));
				addPercentsOfProject(javaCount, xmlCount, dtdCount, propertiesCount, vc, overallCount);				
				Collection holderCollection = holderMap.values();
				Iterator holderCollectionIterator = holderCollection.iterator();
				while (holderCollectionIterator.hasNext()) {
					((LOCEHolder)holderCollectionIterator.next()).setOverallCount(overallCount);
				}
				
								
				template.merge(vc, fw);
				fw.close();
				
			} catch (Exception e) {
				e.printStackTrace();
			}
			
		}
	}

	private void addPercentsOfProject(long javaCount, long xmlCount, long dtdCount, long propertiesCount, VelocityContext vc, long overallCount) {
		double overallPercentJava = (double)javaCount/overallCount;
		vc.put("overallPercentJava", DecimalFormat.getPercentInstance().format(overallPercentJava));
		double overallPercentXml = (double)xmlCount/overallCount;
		vc.put("overallPercentXml", DecimalFormat.getPercentInstance().format(overallPercentXml));
		double overallPercentDtd = (double)dtdCount/overallCount;
		vc.put("overallPercentDtd", DecimalFormat.getPercentInstance().format(overallPercentDtd));
		double overallPercentProperties = (double)propertiesCount/overallCount;
		vc.put("overallPercentProperties", DecimalFormat.getPercentInstance().format(overallPercentProperties));
	}
	
	public void addFileSet(FileSet fileSet) {
		filesetList.add(fileSet);
	}

	/**
	 * @return Returns the reportLocation.
	 */
	public String getReportLocation() {
		return reportLocation;
	}

	/**
	 * @param reportLocation The reportLocation to set.
	 */
	public void setReportLocation(String reportLocation) {
		this.reportLocation = reportLocation;
	}
	
	

}
